using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Lidgren.Network;

namespace TicTacToe.Net
{
    sealed class Connection
    {
        private readonly NetPeer _peer;

        public event EventHandler<NetMessageEventArgs> OnMessageReceived;
        public event EventHandler<NetMessageEventArgs> OnChatMessageReceived;
        public event EventHandler<NetMessageEventArgs> OnPlayerInfoReceived;
        public event EventHandler<NetMessageEventArgs> OnGameTurnReceived;
        public event EventHandler OnPlayerInfoAckReceived;
        public event EventHandler OnGameStarts;
        public event EventHandler OnDisconnected;

        bool _shutdownDone;

        public bool IsHost
        {
            get; private set;
        }

        private void HandleIncomingMsg(string data)
        {
            var msg = NetMessage.Deserialize(data);

            Debug.WriteLine("Handling incoming message. Header: " + msg.Header, "Connection");

            switch (msg.Header)
            {
                case "chat":
                    if (OnChatMessageReceived != null) 
                        OnChatMessageReceived(this, new NetMessageEventArgs(msg));
                    break;
                case "player-info":
                    if (OnPlayerInfoReceived != null) 
                        OnPlayerInfoReceived(this, new NetMessageEventArgs(msg));
                    break;
                case "turn":
                    if (OnGameTurnReceived != null) 
                        OnGameTurnReceived(this, new NetMessageEventArgs(msg));
                    break;
                case "ack-player-info":
                    if (OnPlayerInfoAckReceived != null) 
                        OnPlayerInfoAckReceived(this, new EventArgs());
                    break;
                case "game-starting":
                    if (OnGameStarts != null) 
                        OnGameStarts(this, new EventArgs());
                    break;
                default:
                    if (OnMessageReceived != null) 
                        OnMessageReceived(this, new NetMessageEventArgs(msg));
                    break;
            }
        }

        private void HandleStatusChange(NetConnectionStatus status)
        {
            switch(status)
            {
                case NetConnectionStatus.Disconnected:
                    if (OnDisconnected != null) 
                        OnDisconnected(this, new EventArgs());
                    Shutdown();
                    break;
            }
        }

        private void ReadNextMessage(object peer)
        {
            var msg = _peer.ReadMessage();
            if (msg != null)
            {
                switch (msg.MessageType)
                {
                    case NetIncomingMessageType.StatusChanged:
                        HandleStatusChange(msg.SenderConnection.Status);
                        break;
                    case NetIncomingMessageType.Data:
                        HandleIncomingMsg(msg.ReadString());
                        break;
                }

                _peer.Recycle(msg);
            }
        }

        private Connection()
        {
            IsHost = false;

            var config = new NetPeerConfiguration("3dtictactoe") { MaximumConnections = 1 };
            _peer = new NetPeer(config);
            _peer.RegisterReceivedCallback(ReadNextMessage);
        }

        private Connection(int port)
        {
            IsHost = true;

            var config = new NetPeerConfiguration("3dtictactoe") {MaximumConnections = 1, Port = port};
            _peer = new NetPeer(config);
            _peer.RegisterReceivedCallback(ReadNextMessage);
        }

        #region public interface

        public void Start()
        {
            _peer.Start();
        }

        public void Connect(string host, int port)
        {
            _peer.Start();

            _peer.Connect(host, port);
        }

        public void Shutdown()
        {
            if (_shutdownDone)
                return;

            _peer.Shutdown("123");
            _shutdownDone = true;
        }

        public void SendMessage(NetMessage msg)
        {
            if (!_peer.Connections.Any())
                return;

            Task.Factory.StartNew(() =>
            {
                var data = msg.Serialize();
                var netmsg = _peer.CreateMessage(data.Length);
                netmsg.Write(data);

                _peer.SendMessage(netmsg, _peer.Connections.Single(), NetDeliveryMethod.ReliableOrdered);
            }, TaskCreationOptions.PreferFairness);
        }

        // static 

        public static Connection CreateServer(int port)
        {
            var connection = new Connection(port);
            return connection;
        }

        public static Connection CreateClient()
        {
            var connection = new Connection();
            return connection;
        }

        #endregion
    }
}